using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Threading;
using MonoTouch.UIKit;

namespace SpyTouch
{
	public class SpyTouch
	{
		public static void Run ()
		{
			_inst = new SpyTouch ();
			new Thread (_inst.Watchdog).Start ();
		}

		static SpyTouch _inst;

		Thread _th;

		StreamReader _reader;
		StreamWriter _writer;
		DateTime _lastCommandTime;

		void Watchdog ()
		{
			for (;;) {
				Thread.Sleep (4);
				
				var d = DateTime.UtcNow - _lastCommandTime;
				
				if (d.TotalSeconds > 5) {
					
					if (_th != null) {
						_th.Abort ();
					}

					_th = new Thread (RunLoop);
					_th.Start ();
				}
			}
		}

		void RunLoop ()
		{
			_lastCommandTime = DateTime.UtcNow;

			try {
				using (var client = new TcpClient ("localhost", 20001)) {

					Console.WriteLine ("SpyTouch: Connected to client");

					using (var s = client.GetStream ()) {

						_reader = new StreamReader (s);
						_writer = new StreamWriter (s);

						for (;;) {
							var cmd = _reader.ReadLine ();

							if (string.IsNullOrEmpty (cmd))
								break;

							_lastCommandTime = DateTime.UtcNow;

							try {
								ExecuteCommand (cmd);
							} catch (Exception ex) {
								Console.WriteLine ("! " + ex);
							}
						}
					}
				}
			} catch (Exception ex) {
//				Console.WriteLine ("! SpyTouch: " + ex.Message);
			}
		}

		void ExecuteCommand (string cmd)
		{
			if (string.IsNullOrEmpty (cmd))
				return;

//			Console.WriteLine ("Executing " + cmd);
			if (cmd == "GetVisualTree") {
				var infos = new List<ViewBasicInfo> ();
				WalkVisualTree (v =>
				{
					var basicInfo = new ViewBasicInfo (v);
					infos.Add (basicInfo);
				});
				_writer.WriteLine (Uri.EscapeDataString (string.Join ("\n", infos.Select (i => i.ToString ()).ToArray ())));
				_writer.Flush ();
				
			} else if (cmd.StartsWith ("GetViewInfo")) {
				var parts = cmd.Split ();
				var viewHandle = new IntPtr (long.Parse (parts[1]));
				ViewInfo info = null;
				WalkVisualTree (v =>
				{
					if (v.Handle == viewHandle) {
						info = new ViewInfo (v);
					}
				});
				_writer.WriteLine (Uri.EscapeDataString (info.ToString ()));
				_writer.Flush ();
			}
		}

		class ViewBasicInfo
		{
			public string TypeName { get; private set; }
			public IntPtr Handle { get; private set; }
			public IntPtr SuperHandle { get; private set; }
			public bool Hidden { get; private set; }
			public RectangleF Frame { get; private set; }

			public ViewBasicInfo (UIView view)
			{
				var type = view.GetType ();
				TypeName = type.Name;
				Handle = view.Handle;
				var s = view.Superview;
				SuperHandle = (s != null) ? s.Handle : IntPtr.Zero;
				Hidden = view.Hidden;
				Frame = view.Frame;
			}

			public override string ToString ()
			{
				return string.Format ("{0} {1} {2} {3} {4},{5},{6},{7}", TypeName, Handle, SuperHandle, Hidden, Frame.Left, Frame.Top, Frame.Width, Frame.Height);
			}
		}

		class ViewInfo
		{
			public ViewInfo (UIView view)
			{
			}
		}

		void WalkVisualTree (Action<UIView> viewAction)
		{
			var done = new AutoResetEvent (false);
			UIApplication.SharedApplication.BeginInvokeOnMainThread (delegate {
				foreach (var w in UIApplication.SharedApplication.Windows) {
					WalkVisualTree (w, viewAction);
				}
				done.Set ();
			});
			done.WaitOne ();
		}

		void WalkVisualTree (UIView view, Action<UIView> viewAction)
		{
			if (view != null) {
				try {
					viewAction (view);
				} catch (Exception) {
				}
			}
			foreach (var sv in view.Subviews) {
				WalkVisualTree (sv, viewAction);
			}
		}
	}
}

